﻿using System;
using System.Xml;
using System.Xml.Linq;
using System.Linq;

namespace Admx_Generator
{
    public class Namespace : IEquatable<Namespace>
    {
        private string _target = String.Empty;
        private string _prefix = String.Empty;
        private string _filename = String.Empty;

        /// <summary>
        /// Create a new instance of a namespace from an existing Admx file.
        /// </summary>
        /// <param name="filename">Full path to the ADMX file to read from.</param>
        /// <exception cref="IO and XML related exceptions, among others."/>
        public Namespace(string filename)
        {
            this.FileName = filename;
            this.SetNamespaceFromFile(filename);
        }

        /// <summary>
        /// Create a new instance of a namespace from scratch.
        /// </summary>
        /// <param name="target">Full namespace</param>
        /// <param name="prefix">Prefix for the namespace</param>
        public Namespace(string target, string prefix)
        {
            this._target = target;
            this._prefix = (String.IsNullOrEmpty(prefix) ? this.GetPrefix(target) : this.GetCleanPrefix(prefix));
        }

        #region (isnternal properties)

        /// <summary>
        /// Gets the fully qualified namespace.
        /// </summary>
        public string Target { get { return this._target; } }

        /// <summary>
        /// Gets the prefix for this namespace.
        /// </summary>
        public string Prefix { get { return this._prefix; } }

        /// <summary>
        /// Gets or Sets the filename of the ADMX file.
        /// </summary>
        public string FileName { get { return this._filename; } private set { this._filename = value; } }

        #endregion (Internal Properties)

        #region (methods)
        
        /// <summary>
        /// Read the provided Admx file and sets 'Fullname' and 'Prefix' properties.
        /// </summary>
        /// <param name="filename">Full path to the ADMX file to read from.</param>
        /// <exception cref="IO and XML related exceptions, among others."/>
        private void SetNamespaceFromFile(string filename)
        {
            XDocument xDoc = XDocument.Load(filename);
            XNamespace ns = xDoc.Root.GetDefaultNamespace();
            XElement target = xDoc.Descendants(ns + "target").ToList<XElement>()[0];
            XAttribute namespaceAttribute = target.Attribute("namespace");
            XAttribute prefixAttribute = target.Attribute("prefix");

            this._target = namespaceAttribute.Value;
            this._prefix = this.GetCleanPrefix(prefixAttribute.Value);
        }

        /// <summary>
        /// Return the string after the last '.' character.
        /// </summary>
        /// <param name="fullnamespace">The full namespace where to extract the prefix from.</param>
        /// <returns>The prefix for this namespace.</returns>
        private string GetPrefix(string fullnamespace)
        {
            string result = Guid.NewGuid().ToString();

            if (!string.IsNullOrEmpty(fullnamespace) && fullnamespace.Contains("."))
            {
                result = fullnamespace.Substring(fullnamespace.LastIndexOf('.') + 1);
            }

            return this.GetCleanPrefix(result);
        }

        /// <summary>
        /// Remove all unauthorized characters from a prefix
        /// </summary>
        /// <param name="dirtyPrefix">A prefix to clean</param>
        /// <returns>A prefix without any unauthorized characters</returns>
        private string GetCleanPrefix(string dirtyPrefix)
        {
            char[] dirtyChar = dirtyPrefix.ToCharArray();

            for (int i = 0; i < dirtyChar.Length; i++)
            {
                if (!char.IsLetterOrDigit(dirtyChar[i]) && dirtyChar[i] != '_')
                    dirtyChar[i] = '_';
            }

            return new String(dirtyChar);
        }

        public override string ToString()
        {
            return this.Target;
        }

        #endregion ( methods)

        #region (Interface Implementation)

        public bool Equals(Namespace other)
        {
            if (other == null)
                return false;
            return String.Compare(this.Target, other.Target, StringComparison.InvariantCultureIgnoreCase) == 0;
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as Namespace);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion (Interface Implementation)
    }
}
